Skip to content

Conversation

@Harshdev098
Copy link

Have added in_memory store for testing purpose.
We can edit config file to use specific store either postgresql or memory

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Oct 12, 2025

👋 Thanks for assigning @tnull as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@Harshdev098
Copy link
Author

Harshdev098 commented Oct 12, 2025

Hey @tnull @tankyleo Can you please review it

@tnull tnull self-requested a review October 13, 2025 07:10
Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this!

Generally goes into the right direction, but we def. need to avoid re-allocating everything on every operation.

@Harshdev098 Harshdev098 force-pushed the memory_store branch 2 times, most recently from 4980a75 to 25d57e3 Compare October 14, 2025 06:16
@Harshdev098 Harshdev098 requested a review from tnull October 14, 2025 06:17
@Harshdev098
Copy link
Author

@tnull Have done the required changes

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks much better, but I think we still need to handle global_version properly, even if we're currently not using it client-side.

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @tnull @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tnull @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One comment, will take another look once @tankyleo also had a chance to do a review round here.

@Harshdev098
Copy link
Author

@tankyleo Can you please review it!

@ldk-reviews-bot
Copy link

🔔 3rd Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 4th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 5th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Contributor

@tankyleo tankyleo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay !

version: record.version,
}),
})
} else if request.key == GLOBAL_VERSION_KEY {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like by the time we are here, we know the GLOBAL_VERSION_KEY does not have a value, otherwise guard.get would have returned Some previously. We can just return the GetObjectResponse below directly with version: 0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Harshdev098 double checking things here, we still have this second branch the same as before ?

Comment on lines 261 to 332
let key_prefix = request.key_prefix.unwrap_or_default();
let page_token = request.page_token.unwrap_or_default();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't mind, let's use the more explicit unwrap_or here like we have just below. unwrap_or_default is a little hard to read.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Harshdev098 help me understand we still have the unwrap_or_default here ? If you would prefer unwrap_or_default let me know.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, actually I was debugging the issue of CI and have forgot to use unwrap_or, will update it!

@Harshdev098
Copy link
Author

@tankyleo Have done with the required changes! Can you please review it

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When testing integration with LDK Node locally I found that the tests are currently failing. I now opened #62 to add LDK Node integration tests to our CI here. It would be great if that could land first, and we could also add a CI job for the in-memory store as part of this PR then, ensuring the implementation actually works as expected.

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@tnull
Copy link
Contributor

tnull commented Oct 31, 2025

@Harshdev098 Please rebase now that #62 landed to make use of the new CI checks here.

@Harshdev098 Harshdev098 requested a review from tnull November 4, 2025 03:45
Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also make sure that the LDK Node CI is run against the in-memory store.

AFAICT this is still missing the part that runs the LDK Node integration test against the in-memory store to check functionality in a realistic setting?

@ldk-reviews-bot
Copy link

🔔 5th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@Harshdev098 Harshdev098 force-pushed the memory_store branch 2 times, most recently from 84cfed6 to 939559f Compare November 5, 2025 03:34
Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, and now you're hitting the errors that I originally found when running LDK Node against the in-memory store. Very likely, they are due to the in-memory implementation behaving in an unexpected manner. We need to fix this before this PR can get merged. Please take a look, but let me know if you end up getting stuck or need some assistance figuring this out.

@ldk-reviews-bot
Copy link

🔔 6th Reminder

Hey @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@Harshdev098 Harshdev098 force-pushed the memory_store branch 4 times, most recently from 51a791d to 0168750 Compare November 8, 2025 05:48
@Harshdev098
Copy link
Author

Hey @tnull Have updated the code and the unit test are passing against the ldk node tests but didn't understand what is the cause of integration failures

@Harshdev098 Harshdev098 force-pushed the memory_store branch 3 times, most recently from 92bd1e3 to bd3eca4 Compare November 9, 2025 03:41
@Harshdev098
Copy link
Author

Hey @tnull @tankyleo Have tested it locally, the test are working correctly now, don't know why its stuck in CI! Can you please review it

@Harshdev098 Harshdev098 requested a review from tnull November 9, 2025 03:53
@Harshdev098 Harshdev098 force-pushed the memory_store branch 2 times, most recently from 9ec0e79 to 45fecd1 Compare November 9, 2025 10:18
@ldk-reviews-bot
Copy link

🔔 7th Reminder

Hey @tnull @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tnull @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@ldk-reviews-bot
Copy link

🔔 8th Reminder

Hey @tnull @tankyleo! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Contributor

@tankyleo tankyleo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay again I have cleared all the other priorities, this is now top priority :) Here are some comments, mostly on the types we pass to the different functions.

Will continue review tomorrow 100% ! Thank you again.

ErrorKind::Other,
format!("Failed to drop database {}: {}", db_name, e),
)
Error::new(ErrorKind::Other, format!("Failed to drop database {}: {}", db_name, e))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry the formats in this file were done in a CI-specific PR we merged, go ahead and rebase and drop them thank you :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Harshdev098 sorry i take it back, don't rebase to a new commit just yet :) if you can just drop these changes, keep the same base commit on main, will rebase to a newer base commit as necessary thank you

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh sorry @tankyleo , but I have done it already, my branch is now up to date with main

Comment on lines 267 to 277
let mut vss_put_records: Vec<VssDbRecord> = request
.transaction_items
.into_iter()
.map(|kv| build_vss_record(user_token.clone(), store_id.clone(), kv))
.collect();

let vss_delete_records: Vec<VssDbRecord> = request
.delete_items
.into_iter()
.map(|kv| build_vss_record(user_token.clone(), store_id.clone(), kv))
.collect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not build all these VssDbRecords when we still haven't validated the API input. Instead, let's pass the KeyValue's by reference to the validator functions (ie just read the input to validate it, no cloning / allocations).

Then once we've finished validation, we can build the VssDbRecord and insert into the db as needed.

My fn put function looks like this, feel free to grab it and make the changes you want. I create the VssDbRecord inside the execute_put_object function.

        async fn put(
                &self, user_token: String, request: PutObjectRequest,
        ) -> Result<PutObjectResponse, VssError> {
                if request.transaction_items.len() + request.delete_items.len() > MAX_PUT_REQUEST_ITEM_COUNT
                {
                        return Err(VssError::InvalidRequestError(format!(
                                "Number of write items per request should be less than equal to {}",
                                MAX_PUT_REQUEST_ITEM_COUNT
                        )));
                }

                let store_id = request.store_id.clone();

                let mut guard = self.store.write().await;

                if let Some(version) = request.global_version {
                        validate_put_operation(&guard, &user_token, &store_id, &KeyValue { key: GLOBAL_VERSION_KEY.to_string(), value: Bytes::new(), version, })?;
                }

                for key_value in &request.transaction_items {
                        validate_put_operation(&guard, &user_token, &store_id, key_value)?;
                }

                for key_value in &request.delete_items {
                        validate_delete_operation(&guard, &user_token, &store_id, key_value)?;
                }

                for key_value in request.transaction_items {
                        execute_put_object(&mut guard, &user_token, &store_id, key_value);
                }

                for key_value in &request.delete_items {
                        execute_delete_object(&mut guard, &user_token, &store_id, key_value);
                }

                if let Some(version) = request.global_version {
                        execute_put_object(&mut guard, &user_token, &store_id, KeyValue { key: GLOBAL_VERSION_KEY.to_string(), value: Bytes::new(), version, });
                }

                Ok(PutObjectResponse {})
        }

Comment on lines 273 to 277
let vss_delete_records: Vec<VssDbRecord> = request
.delete_items
.into_iter()
.map(|kv| build_vss_record(user_token.clone(), store_id.clone(), kv))
.collect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also do not need to build VssDbRecords for the delete_items, so let's delete those.

Comment on lines 17 to 28
fn build_vss_record(user_token: String, store_id: String, kv: KeyValue) -> VssDbRecord {
let now = Utc::now();
VssDbRecord {
user_token,
store_id,
key: kv.key,
value: kv.value.to_vec(),
version: kv.version,
created_at: now,
last_updated_at: now,
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we only build the VssDbRecord when we are about to insert into the database, we can delete this function here.


// Validation functions - check for if operations can succeed without modifying data
fn validate_put_operation(
store: &HashMap<String, VssDbRecord>, user_token: &str, store_id: &str, record: &VssDbRecord,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I described earlier, let's pass a key_value: &KeyValue here not a record: &VssDbRecord


fn execute_non_conditional_upsert(
store: &mut HashMap<String, VssDbRecord>, user_token: &str, store_id: &str,
record: &VssDbRecord,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are inserting the KeyValue into the database here, so let's pass a key_value: KeyValue here instead of the record: &VssDbRecord


fn execute_conditional_insert(
store: &mut HashMap<String, VssDbRecord>, user_token: &str, store_id: &str,
record: &VssDbRecord,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reasoning as above, let's pass a key_value: KeyValue here


fn execute_conditional_update(
store: &mut HashMap<String, VssDbRecord>, user_token: &str, store_id: &str,
record: &VssDbRecord,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, a key_value: KeyValue fits better since we are only modifying an existing VssDbRecord, not creating a new one.


fn execute_non_conditional_delete(
store: &mut HashMap<String, VssDbRecord>, user_token: &str, store_id: &str,
record: &VssDbRecord,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, we only need to pass a key_value: &KeyValue, as we are only reading the key, we do not need a &VssDbRecord


fn execute_delete_object(
store: &mut HashMap<String, VssDbRecord>, user_token: &str, store_id: &str,
record: &VssDbRecord,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here let's just pass a key_value: &KeyValue, we do not need a record: &VssDbRecord

Ok(DeleteObjectResponse {})
}

async fn list_key_versions(
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a deadlock here earlier have resolved that! now its running locally

Comment on lines 261 to 332
let key_prefix = request.key_prefix.unwrap_or_default();
let page_token = request.page_token.unwrap_or_default();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, actually I was debugging the issue of CI and have forgot to use unwrap_or, will update it!

ErrorKind::Other,
format!("Failed to drop database {}: {}", db_name, e),
)
Error::new(ErrorKind::Other, format!("Failed to drop database {}: {}", db_name, e))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh sorry @tankyleo , but I have done it already, my branch is now up to date with main

@Harshdev098
Copy link
Author

@tankyleo Have updated the code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants